package com.shigi.knapsack;

import com.shigi.problem.Problem;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Miroslav Šiagi on 27/09/14.
 */
public class KnapsackProblem implements Problem {
    private final KnapsackSolver knapsackSolver;
    private final List<KnapsackInstance> knapsackInstances;
    private List<KnapsackSolution> knapsackSolutions;

    public KnapsackProblem(KnapsackSolver knapsackSolver) {
        super();
        this.knapsackSolver = knapsackSolver;
        this.knapsackInstances = new ArrayList<>();
        this.knapsackSolutions = new ArrayList<>();
    }

    public void solve() {
        loadInputs();
        for(KnapsackInstance knapsackInstance : this.knapsackInstances) {

            long startTime = System.nanoTime();
            KnapsackSolution knapsackSolution = knapsackSolver.solve(knapsackInstance);
            long endTime = System.nanoTime();

            knapsackSolution.setElapsedTime(endTime - startTime);
            this.knapsackSolutions.add(knapsackSolution);
        }

        writeSolutionsToFiles(this.knapsackSolutions);
        writeAverageTimes(this.knapsackSolutions);

        System.out.println("This is the end of solving time.");
    }

    public List<KnapsackInstance> getKnapsackInstances() {
        return this.knapsackInstances;
    }

    public List<KnapsackSolution> getKnapsackSolutions() {return this.knapsackSolutions; }

    private void writeAverageTimes(List<KnapsackSolution> knapsackSolutions) {
        boolean theSameNumberOfItems = true;

        try {
            int numberOfSolutions = knapsackSolutions.size();
            long totalTime = 0l;
            File avgFile = new File(Configuration.AVG_FILES_PATH + "knap" + ".avg.txt");

            if(!avgFile.exists()) {
                avgFile.createNewFile();
            }

            for (int i = 0; i < numberOfSolutions - 1; i++) {

                KnapsackSolution knapsackSolution = knapsackSolutions.get(i);

                if (knapsackSolutions.get(i).getNumberOfItems() == knapsackSolutions.get(i + 1).getNumberOfItems()) {

                    totalTime += knapsackSolution.getElapsedTime();
                } else {

                    totalTime += knapsackSolution.getElapsedTime();

                    BufferedWriter writer = new BufferedWriter(new FileWriter(avgFile, true));

                    String line = "" + totalTime / 50000000.0;

                    totalTime = 0l;
                    writer.append(line + "\n");
                    writer.close();
                }

            }

            for(KnapsackSolution knapsackSolution : knapsackSolutions) {
                if(knapsackSolution.getNumberOfItems() == 30) {
                    totalTime += knapsackSolution.getElapsedTime();
                }
            }

            BufferedWriter writer = new BufferedWriter(new FileWriter(avgFile, true));

            String line = "" + totalTime / 50000000.0;

            writer.append(line + "\n");
            writer.close();

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }

    private void writeSolutionsToFiles(List<KnapsackSolution> knapsackSolutions) {

        for(KnapsackSolution knapsackSolution : knapsackSolutions) {
            try {
                File solFile = new File(Configuration.SOL_FILES_PATH + "knap_" + knapsackSolution.getNumberOfItems() + ".sol.txt");
                File solFileWithTime = new File(Configuration.SOL_FILES_PATH + "knap_" + knapsackSolution.getNumberOfItems() + ".sol.time.txt");

                if(!solFile.exists()) {
                    solFile.createNewFile();
                }

                if(!solFileWithTime.exists()) {
                    solFileWithTime.createNewFile();
                }

                BufferedWriter writer = new BufferedWriter(new FileWriter(solFile, true));
                BufferedWriter writerWithTime = new BufferedWriter(new FileWriter(solFileWithTime, true));

                String line = knapsackSolution.getId() + " " + knapsackSolution.getNumberOfItems() + " " + knapsackSolution.getTotalValue() + " ";
                String line2 = line + knapsackSolution.getElapsedTime();
                writerWithTime.append(line2 + "\n");
                writerWithTime.close();

                String usedItems = Integer.toBinaryString(knapsackSolution.getUsedItems());
                if(usedItems.length() != knapsackSolution.getNumberOfItems()) {
                    int howManyNullsToAdd = knapsackSolution.getNumberOfItems() - usedItems.length();
                    for(int i = 0; i < howManyNullsToAdd; i++) {
                        usedItems = "0" + usedItems;
                    }
                }

                int strUsedItemsLen = usedItems.length();
                for(int i = 0; i < strUsedItemsLen; i++) {
                    line = line + " " + usedItems.charAt(i);
                }

                writer.append(line + "\n");
                writer.close();

            } catch (Exception ex) {
                System.out.println(ex.getMessage());
            }
        }

    }

    private void loadInputs() {
        readInputsFromFiles(Configuration.INST_FILES_PATH);
    }

    protected void readInputsFromFiles(String pathToFiles) {
        File folder = new File(pathToFiles);

        for (File fileEntry : folder.listFiles()) {
            if (!fileEntry.isDirectory()) {
                System.out.println(fileEntry.getName());
                loadInputsFromFile(fileEntry);
            }
        }
    }

    private void loadInputsFromFile(File file) {
        try(BufferedReader br = new BufferedReader(new FileReader(file.getAbsolutePath()))) {
            String line = br.readLine();

            while (line != null) {
                createInstanceFromLine(line);
                line = br.readLine();
            }

        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
    }

    private void createInstanceFromLine(String line) {
        String[] parsedLine = line.split(" ");
        int iterator = 0;
        int itemCounter = 0;
        KnapsackInstance knapsackInstance = new KnapsackInstance(Integer.parseInt(parsedLine[iterator++]));
        knapsackInstance.setNumberOfItems(Integer.parseInt(parsedLine[iterator++]));
        knapsackInstance.setTotalWeight(Integer.parseInt(parsedLine[iterator++]));

        KnapsackItem[] knapsackItems = new KnapsackItem[knapsackInstance.getNumberOfItems()];
        while(iterator < parsedLine.length) {
            KnapsackItem knapsackItem = new KnapsackItem(itemCounter++);
            knapsackItem.setWeight(Integer.parseInt(parsedLine[iterator]));
            knapsackItem.setValue(Integer.parseInt(parsedLine[iterator + 1]));
            knapsackItems[(iterator - 3) / 2] = knapsackItem;
            iterator = iterator + 2;
        }

        if(knapsackItems.length != knapsackInstance.getNumberOfItems()) {
            return;
        }

        knapsackInstance.setKnapsackItems(knapsackItems);
        this.knapsackInstances.add(knapsackInstance);
    }

}
